package com.hoshino.momochi.interceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.hoshino.momochi.annotation.IgnoreLogin;
import com.hoshino.momochi.util.CodeEnum;
import com.hoshino.momochi.util.JsonResponse;
import com.hoshino.momochi.util.LuaScript;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.lang.reflect.Method;

/**
 * 拦截 非@IgnoreLogin注解的方法
 * 校验请求 头 token 是否有效 有效则 将 用户ID 存入 request
 *
 * @return USER_ID
 * @IgnoreLogin注解的方法 会 在 拦截器中直接
 * @return true
 * 非@IgnoreLogin注解方法，会获取 请求头 token
 * 判断token 是否有效
 * 有效则通过
 * 无效则需要登录
 * 若是校验登录
 * count 则会 将 有效token对应的userid放入request中
 * USER_ID userid
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Value("${hoshino.momochi.latetime}")
    private int latetime; // token 过期时间

    private final String USER_ID = CodeEnum.USERID.getMessage();
    private final String TOKEN = CodeEnum.TOKEN.getMessage();

    @Autowired
    private RedisTemplate<String, Serializable> limitRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //支持跨域请求
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
//        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Token, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
        response.addHeader("Access-Control-Allow-Headers", "*");
//        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Origin", "*");

        // 跨域时会首先发送一个OPTIONS请求，这里我们给OPTIONS请求直接返回正常状态
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }

        // 拦截方法
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        // 获取 TOKEN
        String token = request.getHeader(TOKEN);
        if (StringUtils.isNotBlank(token)) {
            // 查询 token 是否 存在 ， 存在 则 将 id 放入 request
            // 否则 login
            // KEYS
            ImmutableList<String> keys = ImmutableList.of(StringUtils.join(token));
            String luaScript = LuaScript.geTokenToCheckLuaScript();
            RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
            // run
            // 执行 lua 脚本
            Number count = limitRedisTemplate.execute(redisScript, keys, latetime); // keys ARGV...
            log.info("拦截器获取token-id:{},{}", token, count);
            if (count != null && count.intValue() > 0) {
                // 将从redis获取到的 用户 id 放入 request中
                request.setAttribute(USER_ID, count);
                return true;
            }
        } else {
            log.info("拦截器获取token为空");
        }
        // 判断接口 是否 有 @IgnoreLogin注解 有 则 不进行检测
        IgnoreLogin annotation = method.getAnnotation(IgnoreLogin.class);
        if (annotation != null) {
            log.info("当前{},有IgnoreLogin注解，跳过登录状态检测", method.getName());
            return true;
        }

        log.info("当前{},没有IgnoreLogin注解，进行登录状态检测", method.getName());

        // 要求登录
        // token 为空 or 失效 需要 重新登录
        // 返回 数据 要求 跳转页面
        // 将JSON数据转换为String 然后 返回
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = mapper.writeValueAsString(JsonResponse.success("Your option is bad"));
        response.getWriter().write(jsonString);
        return false;
    }


}

